/*
 * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */

/****************************************************************************
*
* This file is for ble spp client demo.
*
****************************************************************************/

#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "driver/uart.h"

#include "esp_bt.h"
#include "nvs_flash.h"
#include "esp_bt_device.h"
#include "esp_gap_ble_api.h"
#include "esp_gattc_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_system.h"
#include "esp_gatt_common_api.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"

#define GATTC_TAG                   "GATTC_SPP_DEMO" // LOG 打印的标签
#define PROFILE_NUM                 1
#define PROFILE_APP_ID              0
#define BT_BD_ADDR_STR              "%02x:%02x:%02x:%02x:%02x:%02x"
#define BT_BD_ADDR_HEX(addr)        addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]

#define ESP_GATT_SPP_SERVICE_UUID   0xFFE0  // 进行通信的目标服务UUID
#define SCAN_ALL_THE_TIME           0

struct gattc_profile_inst {
    esp_gattc_cb_t gattc_cb;
    uint16_t gattc_if;
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_start_handle;
    uint16_t service_end_handle;
    uint16_t char_handle;
    esp_bd_addr_t remote_bda;
};

enum{
    SPP_IDX_SVC,               // 0xFFE0
    SPP_IDX_SPP_DATA_RECV_VAL, // 0xFFE1
    SPP_IDX_SPP_DATA_NTY_VAL,  // 0x2902
    SPP_IDX_NB,
};

///Declare static functions
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);

/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */
static struct gattc_profile_inst gl_profile_tab[PROFILE_NUM] = {
    [PROFILE_APP_ID] = {
        .gattc_cb = gattc_profile_event_handler,
        .gattc_if = ESP_GATT_IF_NONE,       /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
    },
};

static esp_ble_scan_params_t ble_scan_params = {
    .scan_type              = BLE_SCAN_TYPE_ACTIVE,
    .own_addr_type          = BLE_ADDR_TYPE_PUBLIC,
    .scan_filter_policy     = BLE_SCAN_FILTER_ALLOW_ALL,
    .scan_interval          = 0x50,
    .scan_window            = 0x30,
    .scan_duplicate         = BLE_SCAN_DUPLICATE_DISABLE
};

//static const char device_name[] = "dreamebt-10060-22"; // 需要扫描找到连接的设备名字
//static const uint8_t device_mac[6] = {0x44, 0x44, 0x44, 0x44, 0x44, 0x44}; // 需要扫描找到连接的MAC地址
//static const uint8_t device_mac[6] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55}; // 需要扫描找到连接的MAC地址
//static const uint8_t device_mac[6] = {0x33, 0x33, 0x33, 0x33, 0x33, 0x33}; // 需要扫描找到连接的MAC地址
//static const uint8_t device_mac[6] = {0x88, 0x88, 0x88, 0x88, 0x88, 0x88}; // 需要扫描找到连接的MAC地址
static const uint8_t device_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x11}; // 需要扫描找到连接的MAC地址

static bool is_connect = false;
static uint16_t spp_conn_id = 0;
static uint16_t spp_mtu_size = 23;
static uint16_t cmd = 0;
static uint16_t spp_srv_start_handle = 0;
static uint16_t spp_srv_end_handle = 0;
static uint16_t spp_gattc_if = 0xff;
static char * notify_value_p = NULL;
static int notify_value_offset = 0;
static int notify_value_count = 0;
static uint16_t count = SPP_IDX_NB;
static esp_gattc_db_elem_t *db = NULL;
static esp_ble_gap_cb_param_t scan_rst;
static QueueHandle_t cmd_reg_queue = NULL;
QueueHandle_t spp_uart_queue = NULL;

static esp_bt_uuid_t spp_service_uuid = {
    .len  = ESP_UUID_LEN_16,
    .uuid = {.uuid16 = ESP_GATT_SPP_SERVICE_UUID,},
};

// void data_to_computer(int16_t data1, int16_t data2)
// {
//     unsigned char  data_to_send[30] = {0};
//     unsigned char i = 0;
//     unsigned char cnt = 0;
//     unsigned char sum = 0;
// 	unsigned char add = 0;
    
//     data_to_send[cnt++]=0xAA;     // 帧头：AA
//     data_to_send[cnt++]=0xAA;     // 帧头：AA
//     data_to_send[cnt++]=0xF1;     // 功能码： 0xF1
//     data_to_send[cnt++]=0;         //需要发送数据的字节数，暂时给0，后面在赋值。

//     data_to_send[cnt++] = (data1>>8)&0xff;
//     data_to_send[cnt++] = data1&0xff;
//     data_to_send[cnt++] = (data2>>8)&0xff;
//     data_to_send[cnt++] = data2&0xff;

//     data_to_send[3] = cnt-4;//计算总数据的字节数。

//     for(i = 0; i < cnt; i++) //对于for语句，当不写大括号的时候，只执行到下面第一个分号结束。
//     {
//         sum += data_to_send[i];
//     }
//     data_to_send[cnt++] = sum;    //计算校验位
	
// 	// for(i = 0; i < cnt; i++)
// 	// {
// 	// 	//LOG("%c", data_to_send[i]);
// 	// }
    
//     uart_write_bytes(UART_NUM_2, (char *)(data_to_send), cnt);
// }
// 四个值上传
void data_to_computer2(int16_t data1, int16_t data2, int16_t data3, int16_t data4)
{
    unsigned char  data_to_send[30] = {0};
    unsigned char i = 0;
    unsigned char cnt = 0;
    unsigned char sum = 0;
	unsigned char add = 0;
    
    data_to_send[cnt++]=0xAA;     // 帧头：AA
    data_to_send[cnt++]=0xAA;     // 帧头：AA
    data_to_send[cnt++]=0xF1;     // 功能码： 0xF1
    data_to_send[cnt++]=0;         //需要发送数据的字节数，暂时给0，后面在赋值。

    data_to_send[cnt++] = (data1>>8)&0xff;
    data_to_send[cnt++] = data1&0xff;
    data_to_send[cnt++] = (data2>>8)&0xff;
    data_to_send[cnt++] = data2&0xff;
    data_to_send[cnt++] = (data3>>8)&0xff;
    data_to_send[cnt++] = data3&0xff;
    data_to_send[cnt++] = (data4>>8)&0xff;
    data_to_send[cnt++] = data4&0xff;

    data_to_send[3] = cnt-4;//计算总数据的字节数。

    for(i = 0; i < cnt; i++) //对于for语句，当不写大括号的时候，只执行到下面第一个分号结束。
    {
        sum += data_to_send[i];
    }
    data_to_send[cnt++] = sum;    //计算校验位
	
	// for(i = 0; i < cnt; i++)
	// {
	// 	//LOG("%c", data_to_send[i]);
	// }
    
    uart_write_bytes(UART_NUM_2, (char *)(data_to_send), cnt);
}

// 压感数据解析
// static void decode_press_data(esp_ble_gattc_cb_param_t * p_data)
// {
//     char buff[5] = {0};
//     char buff2[5] = {0};
//     bool flg = false;
//     uint8_t n = 0, m = 0;
//     for(uint8_t i = 0; i < p_data->notify.value_len; i++)
//     {
//         if(flg == false)
//         {
//             if(p_data->notify.value[i] != 0x2c)
//             {
//                 buff[n] = p_data->notify.value[i];
//                 n++;
//             }
//             else
//             {
//                 flg = true;
//             }                
//         }
//         else
//         {
//             if(p_data->notify.value[i] != 0x0d)
//             {
//                 buff2[m] = p_data->notify.value[i];
//                 m++;
//             }
//             else
//             {
//                 break;
//             }
//         }

//     }
//     // esp_log_buffer_hex(GATTC_TAG, (char *)buff, n);
//     // esp_log_buffer_hex(GATTC_TAG, (char *)buff2, m);

//     int value1 = 0;
//     int value2 = 0;

//     sscanf(buff2, "%d", &value2);
//     sscanf(buff, "%d", &value1);

//     ESP_LOGI(GATTC_TAG, "buff: %d, buff2: %d\n", value1, value2);

//     data_to_computer(value1, value2);
// }

// 压感数据解析
static void decode_press_data(esp_ble_gattc_cb_param_t * p_data)
{
    char ch1RawPress[6] = {0};
    char ch2RawPress[6] = {0};
    char ch1Level[6] = {0};
    char ch2Level[6] = {0};

    uint8_t flg = 0;
    uint8_t n = 0, m = 0, j = 0, k = 0;
    for(uint8_t i = 0; i < p_data->notify.value_len; i++)
    {
        if(flg == 0)
        {
            if(p_data->notify.value[i] != 0x2c) // 找到逗号
            {
                ch1RawPress[n] = p_data->notify.value[i];
                n++;
            }
            else
            {
                flg = 1;
            }                
        }
        else if (flg == 1)
        {
            if(p_data->notify.value[i] != 0x2c) // 找到逗号
            {
                ch2RawPress[m] = p_data->notify.value[i];
                m++;
            }
            else
            {
                flg = 2;
            }
        }
        else if (flg == 1)
        {
            if(p_data->notify.value[i] != 0x2c) // 找到逗号
            {
                ch1Level[m] = p_data->notify.value[i];
                m++;
            }
            else
            {
                flg = 3;
            }
        }
        else
        {
            if(p_data->notify.value[i] != 0x0d)
            {
                ch2Level[k] = p_data->notify.value[i];
                k++;
            }
            else
            {
                break;
            }
        }
    }
    esp_log_buffer_hex(GATTC_TAG, (char *)ch1RawPress, n);
    esp_log_buffer_hex(GATTC_TAG, (char *)ch2RawPress, m);
    esp_log_buffer_hex(GATTC_TAG, (char *)ch1Level, j);
    esp_log_buffer_hex(GATTC_TAG, (char *)ch2Level, k);

    int value1 = 0;
    int value2 = 0;
    int value3 = 0;
    int value4 = 0;

    sscanf(ch1RawPress, "%d", &value1);
    sscanf(ch2RawPress, "%d", &value2);
    sscanf(ch1Level, "%d", &value3);
    sscanf(ch2Level, "%d", &value4);

    ESP_LOGI(GATTC_TAG, "ch1RawPress: %d, ch2RawPress: %d, ch1Level: %d, , ch2Level: %d\n", value1, value2, value3, value4);
    uart_write_bytes(UART_NUM_2, (char *)(p_data->notify.value), p_data->notify.value_len);

    data_to_computer2(value1, value2, value3, value4);
}

static void notify_event_handler(esp_ble_gattc_cb_param_t * p_data)
{
    uint8_t handle = 0;

    // if(p_data->notify.is_notify == true){
    //     ESP_LOGI(GATTC_TAG,"+NOTIFY:handle = %d,length = %d \n", p_data->notify.handle, p_data->notify.value_len);
    // }else{
    //     ESP_LOGI(GATTC_TAG,"+INDICATE:handle = %d,length = %d \n", p_data->notify.handle, p_data->notify.value_len);
    // }
    handle = p_data->notify.handle;
    // if(db == NULL) {
    //     ESP_LOGE(GATTC_TAG, " %s db is NULL\n", __func__);
    //     return;
    // }
    if(handle == db[SPP_IDX_SPP_DATA_RECV_VAL].attribute_handle){
        // 接收到notify数据
        //ESP_LOGI(GATTC_TAG, "handle\n");

        uart_write_bytes(UART_NUM_2, (char *)(p_data->notify.value), p_data->notify.value_len);

        //esp_log_buffer_char(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);

        // ESP_LOGI(GATTC_TAG, "rcv len: %d\n", p_data->notify.value_len);
        esp_log_buffer_char(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);

        // esp_log_buffer_hex(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);

        // decode_acc_data(p_data);

        decode_press_data(p_data);

    }
    else
    {
        // esp_log_buffer_char(GATTC_TAG, (char *)p_data->notify.value, p_data->notify.value_len);
    }
}

static void free_gattc_srv_db(void)
{
    is_connect = false;
    spp_gattc_if = 0xff;
    spp_conn_id = 0;
    spp_mtu_size = 23;
    cmd = 0;
    spp_srv_start_handle = 0;
    spp_srv_end_handle = 0;
    notify_value_p = NULL;
    notify_value_offset = 0;
    notify_value_count = 0;
    if(db){
        free(db);
        db = NULL;
    }
}

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    uint8_t *adv_name = NULL;
    uint8_t adv_name_len = 0;
    esp_err_t err;

    switch(event){
    case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
        if((err = param->scan_param_cmpl.status) != ESP_BT_STATUS_SUCCESS){
            ESP_LOGE(GATTC_TAG, "Scan param set failed: %s", esp_err_to_name(err));
            break;
        }
        //the unit of the duration is second
        uint32_t duration = 0xFFFF;
        ESP_LOGI(GATTC_TAG, "Enable Ble Scan:during time %04" PRIx32 " minutes.",duration);
        esp_ble_gap_start_scanning(duration);
        break;
    }
    case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
        //scan start complete event to indicate scan start successfully or failed
        if ((err = param->scan_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) {
            ESP_LOGE(GATTC_TAG, "Scan start failed: %s", esp_err_to_name(err));
            break;
        }
        ESP_LOGI(GATTC_TAG, "Scan start successed");
        break;
    case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
        if ((err = param->scan_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS) {
            ESP_LOGE(GATTC_TAG, "Scan stop failed: %s", esp_err_to_name(err));
            break;
        }
        ESP_LOGI(GATTC_TAG, "Scan stop successed");
        if (is_connect == false) {
            ESP_LOGI(GATTC_TAG, "Connect to the remote device.");

            ESP_LOGI(GATTC_TAG, "gattc_if: %d, bda: %x %x %x %x %x %x, type: %d\n", gl_profile_tab[PROFILE_APP_ID].gattc_if, \
            scan_rst.scan_rst.bda[0],scan_rst.scan_rst.bda[1],scan_rst.scan_rst.bda[2], \
            scan_rst.scan_rst.bda[3],scan_rst.scan_rst.bda[4],scan_rst.scan_rst.bda[5], \
            scan_rst.scan_rst.ble_addr_type);

            esp_ble_gattc_open(gl_profile_tab[PROFILE_APP_ID].gattc_if, scan_rst.scan_rst.bda, scan_rst.scan_rst.ble_addr_type, true);
        }
        break;
    case ESP_GAP_BLE_SCAN_RESULT_EVT: {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt) {
        case ESP_GAP_SEARCH_INQ_RES_EVT:
            // esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
            // ESP_LOGI(GATTC_TAG, "Searched Adv Data Len %d, Scan Response Len %d", scan_result->scan_rst.adv_data_len, scan_result->scan_rst.scan_rsp_len);
            adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
            // ESP_LOGI(GATTC_TAG, "Searched Device Name Len %d", adv_name_len);
            // esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
            // ESP_LOGI(GATTC_TAG, "\n");

            // ESP_LOGI(GATTC_TAG, "Searched Device MAC: ");
            // esp_log_buffer_hex(GATTC_TAG, scan_result->scan_rst.bda, 6);
            // ESP_LOGI(GATTC_TAG, "\n");

            if (adv_name != NULL) {
                // 根据设备名字连接设备
                // ESP_LOGI(GATTC_TAG, "Need Device: ");
                // esp_log_buffer_char(GATTC_TAG, device_name, sizeof(device_name));
                // ESP_LOGI(GATTC_TAG, "\n");
                // ESP_LOGI(GATTC_TAG, "Adv Device: ");
                // esp_log_buffer_char(GATTC_TAG, adv_name, adv_name_len);
                // ESP_LOGI(GATTC_TAG, "\n");

                // if ( strncmp((char *)adv_name, device_name, adv_name_len) == 0) {
                //     memcpy(&(scan_rst), scan_result, sizeof(esp_ble_gap_cb_param_t));
                //     esp_ble_gap_stop_scanning();
                // }
                // 根据设备mac地址连接设备
                if((device_mac[0] == scan_result->scan_rst.bda[0]) && (device_mac[1] == scan_result->scan_rst.bda[1]) && \
                (device_mac[2] == scan_result->scan_rst.bda[2]) && (device_mac[3] == scan_result->scan_rst.bda[3]) && \
                (device_mac[4] == scan_result->scan_rst.bda[4]) && (device_mac[5] == scan_result->scan_rst.bda[5]))
                {
                    ESP_LOGI(GATTC_TAG, "MAC EQUE!\n");
                    memcpy(&(scan_rst), scan_result, sizeof(esp_ble_gap_cb_param_t));
                    esp_ble_gap_stop_scanning();
                }
            }
            break;
        case ESP_GAP_SEARCH_INQ_CMPL_EVT:
            break;
        default:
            break;
        }
        break;
    }
    case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
        if ((err = param->adv_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS){
            ESP_LOGE(GATTC_TAG, "Adv stop failed: %s", esp_err_to_name(err));
        }else {
            ESP_LOGI(GATTC_TAG, "Stop adv successfully");
        }
        break;
    case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
        ESP_LOGI(GATTC_TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
                  param->update_conn_params.status,
                  param->update_conn_params.min_int,
                  param->update_conn_params.max_int,
                  param->update_conn_params.conn_int,
                  param->update_conn_params.latency,
                  param->update_conn_params.timeout);
        break;
    default:
        break;
    }
}

static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{
    //ESP_LOGI(GATTC_TAG, "EVT %d, gattc if %d", event, gattc_if);

    /* If event is register event, store the gattc_if for each profile */
    if (event == ESP_GATTC_REG_EVT) {
        if (param->reg.status == ESP_GATT_OK) {
            gl_profile_tab[param->reg.app_id].gattc_if = gattc_if;
        } else {
            ESP_LOGI(GATTC_TAG, "Reg app failed, app_id %04x, status %d", param->reg.app_id, param->reg.status);
            return;
        }
    }
    /* If the gattc_if equal to profile A, call profile A cb handler,
     * so here call each profile's callback */
    do {
        int idx;
        for (idx = 0; idx < PROFILE_NUM; idx++) {
            if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
                    gattc_if == gl_profile_tab[idx].gattc_if) {
                if (gl_profile_tab[idx].gattc_cb) {
                    gl_profile_tab[idx].gattc_cb(event, gattc_if, param);
                }
            }
        }
    } while (0);
}

static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{
    esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;

    switch (event) {
    case ESP_GATTC_REG_EVT:
        ESP_LOGI(GATTC_TAG, "REG EVT, set scan params");
        esp_ble_gap_set_scan_params(&ble_scan_params);
        break;
    case ESP_GATTC_CONNECT_EVT:
        ESP_LOGI(GATTC_TAG, "ESP_GATTC_CONNECT_EVT: conn_id=%d, gatt_if = %d", spp_conn_id, gattc_if);
        ESP_LOGI(GATTC_TAG, "REMOTE BDA:");
        // esp_log_buffer_hex(GATTC_TAG, gl_profile_tab[PROFILE_APP_ID].remote_bda, sizeof(esp_bd_addr_t));
        esp_log_buffer_hex(GATTC_TAG, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));

        ESP_LOGI(GATTC_TAG, "conn para: %d", p_data->connect.conn_params.timeout);
        ESP_LOGI(GATTC_TAG, "connection params interval= %d,latency = %d, timeout = %d", \
                p_data->connect.conn_params.interval, \
                p_data->connect.conn_params.latency, \
                p_data->connect.conn_params.timeout);

        spp_gattc_if = gattc_if;
        is_connect = true;
        spp_conn_id = p_data->connect.conn_id;
        memcpy(gl_profile_tab[PROFILE_APP_ID].remote_bda, p_data->connect.remote_bda, sizeof(esp_bd_addr_t));
        esp_ble_gattc_search_service(spp_gattc_if, spp_conn_id, &spp_service_uuid); // 找到目标服务的UUID

        //  esp_ble_conn_update_params_t conn_params = {0};
        // memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
        // /* For the IOS system, please reference the apple official documents about the ble connection parameters restrictions. */
        // conn_params.latency = 0;
        // conn_params.max_int = 0x30;    // max_int = 0x20*1.25ms = 40ms
        // conn_params.min_int = 0x18;    // min_int = 0x10*1.25ms = 20ms
        // conn_params.timeout = 500;    // timeout = 400*10ms = 4000ms
        // ESP_LOGI(GATTC_TAG, "ESP_GATTS_CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:",
        //          param->connect.conn_id,
        //          param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
        //          param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]);
        // gl_profile_tab[PROFILE_APP_ID].conn_id = param->connect.conn_id;
        // //start sent the update connection parameters to the peer device.
        // esp_ble_gap_update_conn_params(&conn_params);       
        break;
    case ESP_GATTC_DISCONNECT_EVT:
        ESP_LOGI(GATTC_TAG, "disconnect reason: 0x%x\n", p_data->disconnect.reason);
        free_gattc_srv_db();
        esp_ble_gap_start_scanning(SCAN_ALL_THE_TIME);
        break;
    case ESP_GATTC_SEARCH_RES_EVT:
        ESP_LOGI(GATTC_TAG, "ESP_GATTC_SEARCH_RES_EVT: start_handle = %d, end_handle = %d, UUID:0x%04x",p_data->search_res.start_handle,p_data->search_res.end_handle,p_data->search_res.srvc_id.uuid.uuid.uuid16);
        spp_srv_start_handle = p_data->search_res.start_handle;
        spp_srv_end_handle = p_data->search_res.end_handle;
        break;
    case ESP_GATTC_SEARCH_CMPL_EVT:
        ESP_LOGI(GATTC_TAG, "SEARCH_CMPL: conn_id = %x, status %d", spp_conn_id, p_data->search_cmpl.status);
        esp_ble_gattc_send_mtu_req(gattc_if, spp_conn_id);
        break;
    case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
        ESP_LOGI(GATTC_TAG,"Index = %d,status = %d,handle = %d\n",cmd, p_data->reg_for_notify.status, p_data->reg_for_notify.handle);
        if(p_data->reg_for_notify.status != ESP_GATT_OK){
            ESP_LOGE(GATTC_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT, status = %d", p_data->reg_for_notify.status);
            break;
        }
        uint16_t notify_en = 1;
        esp_ble_gattc_write_char_descr(
                spp_gattc_if,
                spp_conn_id,
                (db+cmd+1)->attribute_handle,
                sizeof(notify_en),
                (uint8_t *)&notify_en,
                ESP_GATT_WRITE_TYPE_RSP,
                ESP_GATT_AUTH_REQ_NONE);

        break;
    }
    case ESP_GATTC_NOTIFY_EVT:
        // ESP_LOGI(GATTC_TAG,"ESP_GATTC_NOTIFY_EVT\n");
        notify_event_handler(p_data);
        break;
    case ESP_GATTC_READ_CHAR_EVT:
        ESP_LOGI(GATTC_TAG,"ESP_GATTC_READ_CHAR_EVT\n");
        break;
    case ESP_GATTC_WRITE_CHAR_EVT:
        ESP_LOGI(GATTC_TAG,"ESP_GATTC_WRITE_CHAR_EVT:status = %d,handle = %d", param->write.status, param->write.handle);
        if(param->write.status != ESP_GATT_OK){
            ESP_LOGE(GATTC_TAG, "ESP_GATTC_WRITE_CHAR_EVT, error status = %d", p_data->write.status);
            break;
        }
        break;
    case ESP_GATTC_PREP_WRITE_EVT:
        break;
    case ESP_GATTC_EXEC_EVT:
        break;
    case ESP_GATTC_WRITE_DESCR_EVT:
        ESP_LOGI(GATTC_TAG,"ESP_GATTC_WRITE_DESCR_EVT: status =%d,handle = %d \n", p_data->write.status, p_data->write.handle);
        if(p_data->write.status != ESP_GATT_OK){
            ESP_LOGE(GATTC_TAG, "ESP_GATTC_WRITE_DESCR_EVT, error status = %d", p_data->write.status);
            break;
        }
        switch(cmd){
        case SPP_IDX_SPP_DATA_NTY_VAL:
            cmd = SPP_IDX_SPP_DATA_NTY_VAL;
            xQueueSend(cmd_reg_queue, &cmd,10/portTICK_PERIOD_MS);
            break;
        default:
            break;
        };
        break;
    case ESP_GATTC_CFG_MTU_EVT:
        if(p_data->cfg_mtu.status != ESP_OK){
            break;
        }
        ESP_LOGI(GATTC_TAG,"+MTU:%d\n", p_data->cfg_mtu.mtu);
        spp_mtu_size = p_data->cfg_mtu.mtu;

        db = (esp_gattc_db_elem_t *)malloc(count*sizeof(esp_gattc_db_elem_t));
        if(db == NULL){
            ESP_LOGE(GATTC_TAG,"%s:malloc db falied\n",__func__);
            break;
        }
        if(esp_ble_gattc_get_db(spp_gattc_if, spp_conn_id, spp_srv_start_handle, spp_srv_end_handle, db, &count) != ESP_GATT_OK){
            ESP_LOGE(GATTC_TAG,"%s:get db falied\n",__func__);
            break;
        }
        if(count != SPP_IDX_NB){
            ESP_LOGE(GATTC_TAG,"%s:get db count != SPP_IDX_NB, count = %d, SPP_IDX_NB = %d\n",__func__,count,SPP_IDX_NB);
            break;
        }
        cmd = SPP_IDX_SPP_DATA_RECV_VAL;
        ESP_LOGI(GATTC_TAG, "cmd: %d\n", cmd);
        xQueueSend(cmd_reg_queue, &cmd, 10/portTICK_PERIOD_MS);
        break;
    case ESP_GATTC_SRVC_CHG_EVT:
        break;
    default:
        break;
    }
}

void spp_client_reg_task(void* arg)
{
    uint16_t cmd_id = 0;
    ESP_LOGI(GATTC_TAG, "[REG_TASK]...\n");
    for(;;) {
        vTaskDelay(100 / portTICK_PERIOD_MS);
        if(xQueueReceive(cmd_reg_queue, &cmd_id, portMAX_DELAY)) {
            if(db != NULL) {
                if(cmd_id == SPP_IDX_SPP_DATA_RECV_VAL){
                    ESP_LOGI(GATTC_TAG,"Index = %d,UUID = 0x%04x, handle = %d \n", cmd_id, (db+SPP_IDX_SPP_DATA_RECV_VAL)->uuid.uuid.uuid16, (db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle);
                    esp_ble_gattc_register_for_notify(spp_gattc_if, gl_profile_tab[PROFILE_APP_ID].remote_bda, (db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle);
                }
            }
        }
    }
}

void ble_client_appRegister(void)
{
    esp_err_t status;
    char err_msg[20];

    ESP_LOGI(GATTC_TAG, "register callback");

    //register the scan callback function to the gap module
    if ((status = esp_ble_gap_register_callback(esp_gap_cb)) != ESP_OK) {
        ESP_LOGE(GATTC_TAG, "gap register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg)));
        return;
    }
    //register the callback function to the gattc module
    if ((status = esp_ble_gattc_register_callback(esp_gattc_cb)) != ESP_OK) {
        ESP_LOGE(GATTC_TAG, "gattc register error: %s", esp_err_to_name_r(status, err_msg, sizeof(err_msg)));
        return;
    }
    esp_ble_gattc_app_register(PROFILE_APP_ID);

    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(200);
    if (local_mtu_ret){
        ESP_LOGE(GATTC_TAG, "set local  MTU failed: %s", esp_err_to_name_r(local_mtu_ret, err_msg, sizeof(err_msg)));
    }

    cmd_reg_queue = xQueueCreate(10, sizeof(uint32_t));
    xTaskCreate(spp_client_reg_task, "spp_client_reg_task", 2048, NULL, 10, NULL);
}

uint8_t crc_check(uint8_t *content, uint8_t len)
{
    uint8_t crc = 0;
    for(uint8_t i = 0; i < len; i++)
    {
        crc += content[i];
    }
    return crc;
}

static void uart_data_protocol_handle(uint8_t *raw, uint8_t len)
{
    // typedef struct {

    // } uart_data_t;

    // uart_data_t data;

    if(raw[0] == 0xAA) // head
    {
        ESP_LOGI(GATTC_TAG, "CMD %d\n", raw[1]);
        switch (raw[1]) // type
        {
        case 0x01:
            
            // disconnected
        #if 1
            if(is_connect == true)
            {
                ESP_LOGI(GATTC_TAG, "Disconnect the remote device.");
                esp_ble_gattc_close(gl_profile_tab[PROFILE_APP_ID].gattc_if, \
                                    gl_profile_tab[PROFILE_APP_ID].conn_id);
            }
        #endif
            break;
        case 0x02:
            // connect mac addr1
        #if 1
            if (is_connect == false) {
                ESP_LOGI(GATTC_TAG, "Connect to the remote device.");

                esp_bd_addr_t m_addr = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
                esp_ble_addr_type_t m_type = BLE_ADDR_TYPE_PUBLIC;

                esp_ble_gattc_open(gl_profile_tab[PROFILE_APP_ID].gattc_if, \
                                    m_addr, \
                                    m_type, \
                                    true);
            }
        #endif
            break;
        case 0x03:
            // connect mac addr2
            if (is_connect == false) {
                ESP_LOGI(GATTC_TAG, "Connect to the remote device.");

                esp_bd_addr_t m_addr = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
                esp_ble_addr_type_t m_type = BLE_ADDR_TYPE_PUBLIC;

                esp_ble_gattc_open(gl_profile_tab[PROFILE_APP_ID].gattc_if, \
                                    m_addr, \
                                    m_type, \
                                    true);
            }
            break;
        case 0x04:
            // send uuid serivce data
            break;
        case 0x05:
            // 
            break;
        case 0x06:
            // 
            break;
        default:
            break;
        }
    }

}

uint8_t rx_buff[256] = {0};
uint16_t rx_cnt = 0;

void uart_task(void *pvParameters)
{
/*********************************************
 * 连接指定MAC地址的设备 》》 回复连接成功
 * 连接指定名字的设备  》》 回复连接成功
 * 向指定服务UUID发送数据 》》 发送成功
 * 接收到某个UUID发送的数据 》》 回复数据
*/
    uart_event_t event;
    for (;;) {
        //Waiting for UART event.
        if (xQueueReceive(spp_uart_queue, (void * )&event, (TickType_t)portMAX_DELAY)) {
            switch (event.type) {
            //Event of UART receving data
            case UART_DATA:
                
                uart_read_bytes(UART_NUM_2, &rx_buff[rx_cnt], event.size, portMAX_DELAY);

                rx_cnt += event.size;

                ESP_LOGI(GATTC_TAG, "UART0 RX: %d ", rx_cnt);
                esp_log_buffer_hex(GATTC_TAG, rx_buff, rx_cnt);
                ESP_LOGI(GATTC_TAG, "\n");

                if(rx_cnt >= 2)
                {
                   // if(rx_buff[rx_cnt - 1] == crc_check(rx_buff, rx_cnt)) // CRC ok
                    // {
                    //     uart_data_protocol_handle(rx_buff, rx_cnt);

                    //     rx_cnt = 0;
                    //     memset(rx_buff, 0x0, 256);
                    // }
                #if 1
                    if(rx_buff[rx_cnt-1] == 0x0d)
                    {
                        if ((is_connect == true) && (db != NULL) && \
                        ((db+SPP_IDX_SPP_DATA_RECV_VAL)->properties & \
                        (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) {

                            ESP_LOGI(GATTC_TAG, "UART0 RX:");
                            esp_log_buffer_char(GATTC_TAG, rx_buff, rx_cnt);
                            ESP_LOGI(GATTC_TAG, "\n");

                            esp_ble_gattc_write_char( spp_gattc_if,
                                                    spp_conn_id,
                                                    (db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle,
                                                    rx_cnt,
                                                    rx_buff,
                                                    ESP_GATT_WRITE_TYPE_RSP,
                                                    ESP_GATT_AUTH_REQ_NONE);
                        }
                        rx_cnt = 0;
                        memset(rx_buff, 0x0, 256);
                    }
                #endif
                }
                break;
            default:
                break;
            }
        }
    }
    vTaskDelete(NULL);
}

static void spp_uart_init(void)
{
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };
    // uart_config_t uart_config = {
    //     .baud_rate = 115200,
    //     .data_bits = UART_DATA_8_BITS,
    //     .parity = UART_PARITY_DISABLE,
    //     .stop_bits = UART_STOP_BITS_1,
    //     .flow_ctrl = UART_HW_FLOWCTRL_RTS,
    //     .rx_flow_ctrl_thresh = 122,
    //     .source_clk = UART_SCLK_DEFAULT,
    // };

    //Install UART driver, and get the queue.
    uart_driver_install(UART_NUM_2, 4096, 8192, 10, &spp_uart_queue, 0);
    //Set UART parameters
    uart_param_config(UART_NUM_2, &uart_config);
    //Set UART pins
    uart_set_pin(UART_NUM_2, 17, 16, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    //uart_set_pin(UART_NUM_2, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    
    xTaskCreate(uart_task, "uTask", 2048, (void*)UART_NUM_2, 8, NULL);
}

void app_main(void)
{
    esp_err_t ret;

    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();

    nvs_flash_init();
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ESP_LOGI(GATTC_TAG, "%s init bluetooth\n", __func__);
    ret = esp_bluedroid_init();
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }
    ret = esp_bluedroid_enable();
    if (ret) {
        ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ble_client_appRegister();
    spp_uart_init();
}
